﻿// The MIT License (MIT)
// Copyright (c) 2013 Stylianos Dritsas
//
// Permission is hereby granted, free of charge, to any person obtaining a 
// copy of this software and associated documentation files (the "Software"), 
// to deal in the Software without restriction, including without limitation 
// the rights to use, copy, modify, merge, publish, distribute, sublicense, 
// and/or sell copies of the Software, and to permit persons to whom the 
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included 
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
// DEALINGS IN THE SOFTWARE.

//-- System Imports
//--
using System;
using System.Collections.Generic;
using System.Windows.Forms;

//-- Library Imports
//--
using Bentley.Geometry;
using Bentley.GenerativeComponents;
using Bentley.GenerativeComponents.Features;
using Bentley.GenerativeComponents.MicroStation;
using Bentley.GenerativeComponents.GCScript;
using Bentley.Interop.MicroStationDGN;

using BulletSharp;

//-- Disambiguation / Aliases
//--
using Mat4D    = BulletSharp.Matrix;
using Vec3D    = BulletSharp.Vector3;
using World    = BulletSharp.DynamicsWorld;
using Phase    = BulletSharp.BroadphaseInterface;
using Config   = BulletSharp.CollisionConfiguration;
using Shapes   = BulletSharp.AlignedCollisionShapeArray;
using Dispatch = BulletSharp.CollisionDispatcher;
using Solver   = BulletSharp.SequentialImpulseConstraintSolver;

namespace Bentley.GenerativeComponents.Features.Specific 
{
  public class DemoSimulation : Sutd.ManagedFeature
  {
    protected Timer clock;
    protected DemoPhysicsWorld world;

    #region -- Simulation -----------------------------------------------------

    //-- Simulation Animation Controller
    //--
    public DemoSimulation( ) : base( )
    {
      clock = new Timer( );
      clock.Interval = 50;
      clock.Tick += NextFrame;
      clock.Enabled = false;
    }

    //-- Perform Animation Step
    //--
    protected void NextFrame( object sender, EventArgs args )
    {
      //-- Run the Simulation
      //--
      if( world == null ) return;
      var physics = world.physics;
      physics.world.StepSimulation( 1.0f );

      //-- Update Parasolid Bodies
      //--
      int model = mdlModelRef_getActive( );
      foreach( var body in physics.bodies )
      {
        //-- Read the Element from Document
        //--
        int original = 0;
        if( mdlElmdscr_read( ref original, body.solid, model, 0, 0 ) == 0 ) continue;

        //-- Clone the Element (don't ask why, hehe)
        //--
        int modified = 0;
        if( mdlElmdscr_duplicate( ref modified, original ) == 0 )
        {
          //-- Update Transformation Matrix
          //--
          var inverse = Mat4D.Invert( body.matrix );
          body.matrix = body.rigid.WorldTransform;
          inverse *= body.matrix;

          MDL_TRANSFORM transform = new MDL_TRANSFORM(
            inverse.M11, inverse.M21, inverse.M31,
            inverse.M12, inverse.M22, inverse.M32,
            inverse.M13, inverse.M23, inverse.M33,
            inverse.M41, inverse.M42, inverse.M43
          );

          mdlElmdscr_transform( modified, ref transform );

          //-- Rewrite Element
          //--
          body.solid = mdlElmdscr_rewriteByModelRef(
            modified,
            original,
            body.solid,
            model
          );
        }
        mdlElmdscr_freeAll( ref modified );
      }

      //-- Update Views
      //--
      int views = mdlView_getMaxCount( );
      while( --views >= 0 ) mdlView_updateSingle( views );
    }

    [Update]
    public bool Simulate(
      FeatureUpdateContext updateContext,
      DemoPhysicsWorld World,
      int Interval,
      int Update )
    {
      world = World;
      clock.Interval = Interval;
      clock.Enabled = Update != 0;
      return true;
    }

    #endregion
  }

  public class DemoPhysicsWorld : Sutd.ManagedFeature
  {
    public Sutd.Physics physics;

    #region -- Construction ---------------------------------------------------

    //-- Clean up before delete
    //--
    protected override void OnBeingDeleted( )
    {
      if( physics != null )
      {
        physics.Dispose( );
      }
      base.OnBeingDeleted( );
    }

    [Update]
    public bool Build(
      FeatureUpdateContext updateContext,
      [ParentModel] Point[] Points, 
      double Size )
    {
      //-- Upkeep
      //--
      if( physics == null )
      {
        physics = new Sutd.Physics( );
      }
      else
      {
        Regenerate( );
        physics.Reset( );
      }

      //-- Set Gravity
      //--
      physics.world.Gravity = new Vec3D( 0, 0, -10 );

      //-- Create Ground 
      //--
      var body = new Sutd.Physics.Body( );
      {
        //-- Define Infinite Plane
        //--
        var shape = new StaticPlaneShape( new Vec3D( 0, 0, 1 ), 0 );
        physics.shapes.Add( shape );

        //-- Set Physics State / Bullet
        //-- Fixed bodies have zero mass and inertia
        //-- 
        var param = new RigidBodyConstructionInfo( 
          mass: 0.0f, motionState: new DefaultMotionState( Mat4D.Identity ),
          collisionShape: shape, localInertia: Vec3D.Zero );
        body.rigid = new RigidBody( param );
        param.Dispose( );

        physics.world.AddRigidBody( body.rigid );
        body.matrix = body.rigid.WorldTransform;

        //-- Set Visual State / Rhino
        //-- Create a very thin but wide finite box
        //--
        var transform = DTransform3d.Identity;       
        AddBox( transform, new DVector3d( 50, 50, 0.01 ) );
        body.solid = geometry[geometry.Count - 1];
      }
      physics.bodies.Add( body );

      //-- Create 3D Grid of Boxes
      //--
      float half = (float)( Size * 0.5 );
      foreach( var point in Points )
      {
        body = new Sutd.Physics.Body( );
        {
          //-- Collision Shape
          //--
          var shape = new BoxShape( half, half, half );
          physics.shapes.Add( shape );

          //-- Mass Properties
          //--
          var inertia = Vec3D.Zero;
          shape.CalculateLocalInertia( mass: 1.0f, inertia: out inertia );

          //-- Physics State
          //--
          var param = new RigidBodyConstructionInfo(
            mass: 1.0f, motionState: new DefaultMotionState( Mat4D.Identity ),
            collisionShape: shape, localInertia: inertia );
          body.rigid = new RigidBody( param );
          param.Dispose( );

          physics.world.AddRigidBody( body.rigid );

          body.rigid.Translate( new Vec3D( (float)point.X, (float)point.Y, (float)point.Z ) );
          body.matrix = body.rigid.WorldTransform;

          //-- Visual State
          //--
          var transform = DTransform3d.Identity;
          transform.Translation = point.DPoint3d;
          AddBox( transform, new DVector3d( Size, Size, Size ) );
          body.solid = geometry[geometry.Count - 1];
        }
        physics.bodies.Add( body );
      }

      return true;
    }

    #endregion
  }
}
